<?php

/**
 * View[HTML] :: Filter[Warlock]
 *
 * This is the primary filter and should always be the first filter
 * called. The filter is responsible for all the model and component
 * tags.
 *
 * Warlock is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 *
 * @package    warlock
 * @author     David Cole <neophyte@sourcetutor.com>
 * @license    GNU/GPL v2
 */

// Grab filter dependencies
using('Engine.Component');
using('Engine.Profiling');
using('Library.XSL');

class View_HTML_Warlock extends Object
{
    /**#@+
     * Required Core Objects
     *
     * These are references to the core objects required by this
     * class.
     *
     * @access private
     * @var    object
     */
    var $_Error;
    var $_Model;
    var $_View;
    var $_Component;
    /**#@-*/

    /**
     * Application
     *
     * @var string
     */
    var $_Application;

    /**
     * Constructor
     *
     * @return void
     * @access public
     */
    function __construct()
    {
        // Get references to required objects
        $this->_Error     = &Vault::Get('Debug_Error');
        $this->_Model     = &Vault::Get('Model');
        $this->_Component = &Vault::Get('Component');
        $this->_View      = &Vault::Get('View');
        $this->_Profiler  = &Vault::Get('Debug_Profiling');
        $this->_User      = &Vault::Get('User');

        // Grab the active application
        $this->_Application = $this->_Model->Get('Application');
    }

    function Process($input)
    {        
        /*
         * Iterate over model body items and replace the appropriate
         * tag with the generated content.
         */

        $temp       = '';
        $body_items = $this->_Model->Get('Page.Body');

        for ($i = 0, $max = count($body_items); $i < $max; $i++)
        {
            switch ($body_items[$i]['type'])
            {
                case 'text':
                    $temp .= $body_items[$i]['data'];
                    break;

                case 'module':

            	    $this->_Profiler->Start('Transform \'mod_'.$body_items[$i]['renderer'].'_'.$body_items[$i]['name'].'\'');

            	    if (($file = $this->_View->ThemeFile('Transform/mod_'.$body_items[$i]['renderer'].'_'.$body_items[$i]['name'].'.php')) !== false)
            	        $temp .= $this->_ProcessPHP(W_ENV_PATH.$file, $body_items[$i]['data']);
            	    else
            	    {
            	        if (($file = $this->_View->ThemeFile('Transform/mod_'.$body_items[$i]['renderer'].'_'.$body_items[$i]['name'].'.xsl')) !== false)
            	            $temp .= $this->_ProcessXSL(W_ENV_PATH.$file, $body_items[$i]['data']);
            	        else
            	            $this->_Error->Log('View[HTML] :: Error locating file capable of transforming \'mod_'.$body_items[$i]['renderer'].'_'.$body_items[$i]['name'].'\'', W_LOG_CRIT);
            	    }

            	    $this->_Profiler->Stop();

                    break;

                case 'library':

            	    $this->_Profiler->Start('Transform \'lib_'.$body_items[$i]['renderer'].'_'.$body_items[$i]['name'].'\'');

            	    if (($file = $this->_View->ThemeFile('Transform/lib_'.$body_items[$i]['renderer'].'_'.$body_items[$i]['name'].'.php')) !== false)
            	    {
            	        $temp .= $this->_ProcessPHP(W_ENV_PATH.$file, $body_items[$i]['data']);
            	    }
            	    else if (($file = $this->_View->ThemeFile('Transform/lib_'.$body_items[$i]['renderer'].'.php')) !== false)
            	    {
            	        $temp .= $this->_ProcessPHP(W_ENV_PATH.$file, $body_items[$i]['data']);
            	    }
            	    else if (($file = $this->_View->ThemeFile('Transform/lib_'.$body_items[$i]['renderer'].'_'.$body_items[$i]['name'].'.xsl')) !== false)
            	    {
            	        $temp .= $this->_ProcessXSL(W_ENV_PATH.$file, $body_items[$i]['data']);
            	    }
            	    else if (($file = $this->_View->ThemeFile('Transform/lib_'.$body_items[$i]['renderer'].'.xsl')) !== false)
            	    {
            	        $temp .= $this->_ProcessXSL(W_ENV_PATH.$file, $body_items[$i]['data']);
            	    }
            	    else
                        $this->_Error->Log('View[HTML] :: Error locating file capable of transforming \'lib_'.$body_items[$i]['renderer'].'_'.$body_items[$i]['name'].'\'', W_LOG_CRIT);

            	    $this->_Profiler->Stop();

                    break;
            }
        }

        $input = str_replace('{model[Page.Body]->Display()}', $temp, $input);

        /*
         * Iterate over the component and model tags replacing them
         * with the appropriately generated content.
         */

        $filters   = array();
    	$filters[] = '/\{(component.*)\}/iU';
    	$filters[] = '/\{(model.*)\}/iU';

    	foreach ($filters as $pattern)
    	{    	    
            if (preg_match_all($pattern, $input, $results))
        	{
                // No sense it generating content for identical tags more than once
        	    $original = array_values(array_unique($results[0]));
        	    $matches  = array_values(array_unique($results[1]));

            	for ($i = 0, $max = count($matches); $i < $max; $i++)
            	{
            	    // Grab tag to be worked on
            	    $tag = $matches[$i];

            	    // Reset Tag Variables
            	    $type    = '';       // model|component
            	    $options = '';       // data between []
            	    $method  = '';       // method after ->
            	    $args    = array();  // array of the comma delimited values for the method

            	    /*
            	     * Parse the info required for the above variables
            	     * from the matched item.
            	     */

            	    preg_match('/([A-Z0-9]+)\[(.*)\]\-\>([A-Z0-9]+)\(([,A-Z0-9]*)\)/iU', $tag, $tag_params);

            	    $type    = $tag_params[1];
            	    $options = $tag_params[2];
            	    $method  = $tag_params[3];
            	    $args    = explode(',', $tag_params[4]);

            	    /*
            	     * Based on the type and options found within the tag attempt
            	     * to get relevent content to be passed to the method.
            	     */

            	    $temp = '';

            	    switch ($type)
            	    {
            	        case 'model':
            	            $temp = $this->_Model->Get($options);
            	            break;

            	        case 'component':

            	            $parts = explode('.', $options);

            	            $p_com  = '';
            	            $p_met  = '';
            	            $p_app  = $this->_Application;
            	            $p_arg  = array();

            	            switch (count($parts))
            	            {
            	                case 1:
            	                    $p_met = substr($parts[0], 0, ($p = strpos($parts[0], '(')));
            	                    $p_arg = explode(',', substr($parts[0], $p + 1, -1));
            	                    $p_com = $p_met;
            	                    break;
            	                case 2:
            	                    $p_com = $parts[0];
            	                    $p_met = substr($parts[1], 0, ($p = strpos($parts[1], '(')));
            	                    $p_arg = explode(',', substr($parts[1], $p + 1, -1));
            	                    break;
            	                case 3:
            	                    $p_app = $parts[0];
            	                    $p_com = $parts[1];
            	                    $p_met = substr($parts[2], 0, ($p = strpos($parts[2], '(')));
            	                    $p_arg = explode(',', substr($parts[2], $p + 1, -1));
            	                    break;
            	            }

            	            if ($method == 'Transform')
            	                $args[0] = 'com_'.$p_com.'_'.$p_met.((!empty($args[0]))?'_'.$args[0]:'');

            	            $temp = $this->_Component->Run($p_app, $p_com, $p_met, $p_arg);
            	            break;
            	    }

            	    /*
            	     * Process the retrieved content using the specified method
            	     * and arguments.
            	     */

            	    switch ($method)
            	    {
            	        case 'Display':
            	            $input = str_replace($original[$i], is_array($temp)?implode(',', $temp):$temp, $input);
            	            break;

            	        case 'Internal':
                            if (method_exists(&$this, '_Internal_'.$args[0]))
                                $input = str_replace($original[$i], call_user_func(array(&$this, '_Internal_'.$args[0]), $temp), $input);
            	            break;

            	        case 'Transform':

            	            $this->_Profiler->Start('Transform \''.$args[0].'\'');
            	            if (($file = $this->_View->ThemeFile('Transform/'.$args[0].'.php')) !== false)
            	                $input = str_replace($original[$i], $this->_ProcessPHP(W_ENV_PATH.$file, $temp), $input);
            	            else
            	            {
            	                if (($file = $this->_View->ThemeFile('Transform/'.$args[0].'.xsl')) !== false)
            	                    $input = str_replace($original[$i], $this->_ProcessXSL(W_ENV_PATH.$file, $temp), $input);
            	                else
            	                    $this->_Error->Log('View[HTML] :: Error locating file capable of transforming \''.$args[0].'\'', W_LOG_CRIT);
            	            }
                            $this->_Profiler->Stop();
            	            break;
            	    }
            	}
        	}
        	else
        	{
        	    $this->_Error->Log('View[HTML] : Filter[Warlock] :: Nothing Matches \''.$pattern.'\'', W_LOG_DEBUG);
        	}
    	}

    	
    	$input = $this->_Relative2Absolute($input);
    	
    	return $input;
    }

    function _ProcessPHP($file, $data)
    {        
        return include $file;
    }

    function _Internal_Header($data)
    {
        $html = '';

        // Process JS Files
        for ($i = 0, $imax = count($data['JS']); $i < $imax; $i++)
            $files[] = '\''.$this->_View->ThemeFile('JS/'.$data['JS'][$i]).'\'';        
        
        $html .= '<script type="text/javascript" src="/Themes/Default/JS/loader.js"></script>'."\n";
        $html .= '<script language="javascript" type="text/javascript">'."\n";
        $html .= '  WJSLoader.Include({'."\n";
        $html .= '    files : new Array('.implode(',', $files).')});'."\n";
        $html .= '</script>';          
                                 
        // Process Raw Text Sections
        $html .= implode("\n", $data['Raw']);

        // Process CSS Files
        $html .= '<style type="text/css" media="all">'."\n";
        
        for ($i = 0, $imax = count($data['CSS']); $i < $imax; $i++)
            $html .= '  @import "'.$this->_View->ThemeFile('CSS/'.$data['CSS'][$i]).'";'."\n";
        
        $html .= '</style>';        
        
        return $html;        
    }
    
    function _ProcessXSL($xsl, $xml)
    {
        // Ensure Data is XML
        if (is_array($xml))
            $xml = array2xml('root', $xml);

        /*
         * Grab the XSL file and look for special tags in the comments
         * for including additional CSS and JS files.
         */

        $data = file_get_contents($xsl);
        $data = substr($data, ($p = strpos($data, '<!--') + 4), (strpos($data, '-->') - $p));

        $dependencies = explode("\n", $data);

        for ($i = 0, $max = count($dependencies); $i < $max; $i++)
        {
            $dependencies[$i] = trim($dependencies[$i]);

            if (empty($dependencies[$i]))
                continue;

            list($type, $location) = explode('=', $dependencies[$i]);

            switch (trim($type))
            {
                case 'model[page/stylesheet]':
                    $this->_Model->Set('Page.Header.CSS', array($this->_View->ThemeFile(trim($location))));
                    break;
                case 'model[page/javascript]':
                    $this->_Model->Set('Page.Header.JS', array($this->_View->ThemeFile(trim($location))));
                    break;
            }
        }

        /*
         * Transform the stylesheet against the given XML data
         * and return the generated data.
         */

        return transform_xsl_stylesheet($xml, $xsl);
    } 
    
    /**
     * _Relative2Absolute
     * 
     * Scans over the generated page looking for relative paths which
     * need to be converted to the absolute theme path.
     *
     * @param  string  $input  Template to be formatted
     * 
     * @return string
     */
    function _Relative2Absolute($input)
    {
        /*
         * Search and Replace any relative image paths
         */
    
    	if (preg_match_all('/<img src\s*="\s*(?:\'|")?(?P<src>[^\'">]*?)(?:\'|"|\\s) ([^>]*)>/i', $input, $results))
    	{
    	    $this->_Profiler->Start('Process '.count($results[0]).' Relative Image Paths');

    		$original = $results[0];
    		$matches  = $results['src'];
    
    		foreach ($matches as $k => $image)
    		{
    		    if (($temp = $this->_View->ThemeFile($image)) !== false)
    		        $input = str_replace($original[$k], str_replace($image, $temp, $original[$k]), $input);
    		    else
                    $this->_Error->Log('View[HTML] : Filter[Warlock] :: Error Locating Image \''.$image.'\'', W_LOG_ERR);
    		}    	

    		$this->_Profiler->Stop();   		
    	}
    		
    	return $input;
    }    
    
    

    

}
?>

